/*
As3 Performances Analyzer
Copyright (C) 2011 Ankama Studio

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Contact : seyraud@ankama.com
Project : http://code.google.com/p/as3-performances-analyzer
*/

package ui.components
{
	import flash.desktop.NativeApplication;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.ProgressEvent;
	import flash.filters.DropShadowFilter;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	import flash.text.TextFormatAlign;
	import flash.utils.Dictionary;
	import flash.utils.getTimer;
	import flash.utils.setTimeout;
	
	import interfaces.IFrameActionContainer;
	import interfaces.ITimelineData;
	
	import mx.flash.UIMovieClip;
	import mx.managers.PopUpManager;
	
	import spark.core.SpriteVisualElement;
	
	import types.ActionBuffer;
	import types.Frame;
	import types.FrameAction;
	import types.TimeBar;
	
	import ui.popup.ProgressPopup;
	
	public class GraphBar extends SpriteVisualElement
	{
		private static const MAX_RENDER_PER_FRAME : uint = 1000;
		
		private var _serie : Vector.<ITimelineData>;
		private var _transformedSerie : Vector.<ITimelineData>;
		private var _overCursor : Sprite;
		private var _overCursorText : TextField;
		private var __width : Number = 0;
		private var __height : Number = 0;
		private var _container : GraphBarContainer;
		private var _opened : Boolean;
		
		public static var DISPLAY_TOOLTIPS : Boolean = true;
		public static var DISPLAY_NEAREST_TOOLTIPS : Boolean = true;
		
		public var graphId : String;
		
		public override function get width():Number
		{
			return __width;
		}
		public override function get height():Number
		{
			return __height;
		}
		
		public function get serie() : Vector.<ITimelineData>
		{
			return _serie;
		}
		
		public function GraphBar(s : Vector.<ITimelineData>, container : GraphBarContainer)
		{
			super();
			_container = container;
			_serie = s;
			_overCursor = new Sprite;
			_overCursor.visible = false;
			_overCursor.mouseEnabled = false;
			_overCursor.mouseChildren = false;
			_overCursorText = new TextField;
			_overCursorText.selectable = false;
			_overCursorText.height = 20;
			_overCursorText.border = true;
			_overCursorText.borderColor = 0xeeeeee;
			_overCursorText.background = true;
			_overCursorText.backgroundColor = 0xfefefe;
			_overCursorText.y = -30;
			_overCursorText.autoSize = TextFieldAutoSize.LEFT;
			_overCursorText.multiline = true;
			
			var tf  : TextFormat = new TextFormat;
			tf.align = TextFormatAlign.CENTER;
			tf.font = "verdana";
			_overCursorText.defaultTextFormat = tf;
			_overCursorText.filters = [new DropShadowFilter(1,45,0,.5,5,5)];
			_overCursor.addChild(_overCursorText);
			
			addEventListener(Event.ADDED_TO_STAGE, onStageAdded);
			addEventListener(MouseEvent.MOUSE_OVER, onOver);
			addEventListener(MouseEvent.MOUSE_OUT, onOut);
			addEventListener(MouseEvent.DOUBLE_CLICK, onDblClick);
			
			__width = s[s.length - 1].end;
		}
		
		public function destroy() : void
		{
			_serie = null;
			_transformedSerie = null;
			removeEventListener(Event.ADDED_TO_STAGE, onStageAdded);
			removeEventListener(MouseEvent.MOUSE_OVER, onOver);
			removeEventListener(MouseEvent.MOUSE_OUT, onOut);
			removeEventListener(MouseEvent.DOUBLE_CLICK, onDblClick);
		}
		
		private function onStageAdded(e:Event) : void
		{
			render();
		}
		
		private function render() : void
		{
			if(_serie)
			{
				var ts : uint = getTimer();
				prepareData();
				trace((getTimer() - ts) + " ms");
				renderStep();
			}
		}
		
		private function prepareData() : void
		{
			var bufferStack : Dictionary = new Dictionary;
			var buffer : ActionBuffer;
			
			_transformedSerie = new Vector.<ITimelineData>;
			var i : uint = 0;
			while(i < _serie.length)
			{
				var data : ITimelineData = _serie[i];
				++i;
				buffer = bufferStack[data.depth];
				if(!buffer)
				{
					buffer = new ActionBuffer(data.duration == 0 ?
												ActionBuffer.TYPE_SHORT
											  : ActionBuffer.TYPE_SAME);
					buffer.add(data);
					bufferStack[data.depth] = buffer;
					continue;
				}
				
				if(buffer.depth == data.depth)
				{
					if(buffer.type == ActionBuffer.TYPE_SHORT && buffer.length == 1 && buffer.lastData.name == data.name)
						buffer.type = ActionBuffer.TYPE_SAME;
					
					if(buffer.type == ActionBuffer.TYPE_SHORT)
					{
						if(data.duration == 0 && data.start - buffer.end < 2 && data.depth)
								buffer.add(data);
						else
						{
							_transformedSerie.push(buffer.length > 1 ? buffer : buffer.lastData);
							--i;
							bufferStack[data.depth] = null;
						}
					}
					else
					{
						if(buffer.lastData.name == data.name && data.start - buffer.end < 2 && data.duration < 5 && data.depth)
							buffer.add(data);
						else
						{
							_transformedSerie.push(buffer.length > 1 ? buffer : buffer.lastData);
							--i;
							bufferStack[data.depth] = null;
						}
					}
				}
			}
			
			for each(buffer in bufferStack)
				if(buffer)
					_transformedSerie.push(buffer.length > 1 ? buffer : buffer.lastData);
		}
		
		private function renderStep(startIndex:uint=0) : void
		{
			var max : uint = MAX_RENDER_PER_FRAME;
			var i : uint = startIndex;
			var ts:uint = getTimer();
			while(i < _transformedSerie.length && --max)
			{
				var data : ITimelineData = _transformedSerie[i];
				++i;
				
				var bar : TimeBar = new TimeBar(data);
				bar.buttonMode = data.depth == 0;
				bar.x = data.start;
				bar.y = (30 + 1) * data.depth;
				__height = __height < bar.y + bar.height + 1 ? __height = bar.y + bar.height + 1 : __height;
				addChild(bar);
			}
			trace("took " + (getTimer() - ts) + " ms");
			
			if(startIndex < _transformedSerie.length)
			{
				setTimeout(renderStep, 20, i + 1);
				dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS, false, false, i, _transformedSerie.length));
			}
			else
			{
				trace("Total shape : " + numChildren);
				dispatchEvent(new Event(Event.COMPLETE));
			}
		}
		
		private function onDblClick(e:MouseEvent) : void
		{
			if(TimeBar(e.target).data.children.length && TimeBar(e.target).data.depth == 0)
				if(!TimeBar(e.target).opened)
				{
					_container.addSerie(TimeBar(e.target).data.children as Vector.<ITimelineData>, TimeBar(e.target).data.legend);
					TimeBar(e.target).opened = true;
				}
				else
				{
					_container.removeSerie(TimeBar(e.target).data.legend);
					TimeBar(e.target).opened = false;
				}
		}
		
		private function onOut(e:Event) : void
		{
			_overCursor.visible = false;
		}
		private function onOver(e:Event) : void
		{
			if(e.target is TimeBar && TimeBar(e.target).legend)
			{
				stage.addChild(_overCursor);
				_overCursor.visible = true;
				_overCursor.x = stage.mouseX;
				_overCursor.y = stage.mouseY;
				_overCursorText.text = TimeBar(e.target).legend;
				_overCursorText.width = _overCursorText.textWidth + 5;
				_overCursorText.x = -_overCursorText.width / 2;
			}
		}
	}
}